﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Diagnostics;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace Client
{
    public enum LibType { LoadBmp, LoadMemory, LoadManual, UseCache }
    public enum InternationalVersion { Korean, Taiwan, Chinese, English }

    public sealed class ImageArray : IDisposable
    {
        [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
        private sealed class ImageHeader
        {
            public static int MySize { get { return Marshal.SizeOf(typeof(ImageHeader)); } }

            public byte TitleLength;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
            public string Title;
            public int ImageCount;
            public int ColorCount;
            public int PaletteSize;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
        private sealed class ImageHeaderEx
        {
            public static int MySize { get { return Marshal.SizeOf(typeof(ImageHeaderEx)); } }

            public byte TitleLength;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
            public string Title;
            public int ImageCount;
            public int ColorCount;
            public int PaletteSize;
            public uint VersionInfo;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private sealed class ImageInfo
        {
            public static int MySize { get { return Marshal.SizeOf(typeof(ImageInfo)); } }

            public ushort Width;
            public ushort Height;
            public ushort PosX;
            public ushort PosY;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private sealed class ImageInfoEx
        {
            public static int MySize { get { return Marshal.SizeOf(typeof(ImageInfoEx)); } }

            public ushort Width;
            public ushort Height;
            public ushort PosX;
            public ushort PosY;
            public uint ImageVersion;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
        private sealed class IndexHeader
        {
            public static int MySize { get { return Marshal.SizeOf(typeof(IndexHeader)); } }

            public byte TitleLength;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
            public string Title;        //'WEMADE Entertainment inc.'
            public int IndexCount;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
        private sealed class IndexHeaderEx
        {
            public static int MySize { get { return Marshal.SizeOf(typeof(IndexHeaderEx)); } }

            public byte TitleLength;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
            public string Title;        //'WEMADE Entertainment inc.'
            public int IndexCount;
            public uint VersionInfo;
        }

        private sealed class DirectXImage
        {
            public ushort PosX;
            public ushort PosY;
            public Texture Surface;
            public int LatestTime;
        }

        private sealed class BmpImage
        {
            public Bitmap Bmp;
            public int LatestTime;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 8)]
        private struct PalColor
        {
            public static int MySize { get { return Marshal.SizeOf(typeof(PalColor)); } }

            public byte Blue;
            public byte Green;
            public byte Red;
            public byte Alpha;

            public int ToArgb()
            {
                return (Alpha << 24 | Blue << 16 | Green << 8 | Red);
            }
        }

        public static int WilVersion = 20020;
        public static int WixVersion = 20021;

        public const int WilTaiwan = 20030;
        public const int WixTaiwan = 20031;

        public const int WilChinesse = 20040;
        public const int WixChinesse = 20041;

        public const int WilEnglish = 20050;
        public const int WixEnglish = 20051;

        public const uint WilMagicCode = 0xC02a1173;
        public const uint WixMagicCode = 0xB13a11F0;


        // NOT USED
        /*private static uint[] Palette = new uint[256]
        {
            0x00000000,
            0x00800000,
            0x00008000,
            0x00808000,
            0x00000080,
            0x00800080,
            0x00008080,
            0x00C0C0C0,
            0x00558097,
            0x009DB9C8,
            0x007B7373,
            0x002D2929,
            0x005A5252,
            0x00635A5A,
            0x00423939,
            0x001D1818,
            0x00181010,
            0x00291818,
            0x00100808,
            0x00F27971,
            0x00E1675F,
            0x00FF5A5A,
            0x00FF3131,
            0x00D65A52,
            0x00941000,
            0x00942918,
            0x00390800,
            0x00731000,
            0x00B51800,
            0x00BD6352,
            0x00421810,
            0x00FFAA99,
            0x005A1000,
            0x00733929,
            0x00A54A31,
            0x00947B73,
            0x00BD5231,
            0x00522110,
            0x007B3118,
            0x002D1810,
            0x008C4A31,
            0x00942900,
            0x00BD3100,
            0x00C67352,
            0x006B3118,
            0x00C66B42,
            0x00CE4A00,
            0x00A56339,
            0x005A3118,
            0x002A1000,
            0x00150800,
            0x003A1800,
            0x00080000,
            0x00290000,
            0x004A0000,
            0x009D0000,
            0x00DC0000,
            0x00DE0000,
            0x00FB0000,
            0x009C7352,
            0x00946B4A,
            0x00734A29,
            0x00523118,
            0x008C4A18,
            0x00884411,
            0x004A2100,
            0x00211810,
            0x00D6945A,
            0x00C66B21,
            0x00EF6B00,
            0x00FF7700,
            0x00A59484,
            0x00423121,
            0x00181008,
            0x00291808,
            0x00211000,
            0x00392918,
            0x008C6339,
            0x00422910,
            0x006B4218,
            0x007B4A18,
            0x00944A00,
            0x008C847B,
            0x006B635A,
            0x004A4239,
            0x00292118,
            0x00463929,
            0x00B5A594,
            0x007B6B5A,
            0x00CEB194,
            0x00A58C73,
            0x008C735A,
            0x00B59473,
            0x00D6A573,
            0x00EFA54A,
            0x00EFC68C,
            0x007B6342,
            0x006B5639,
            0x00BD945A,
            0x00633900,
            0x00D6C6AD,
            0x00524229,
            0x00946318,
            0x00EFD6AD,
            0x00A58C63,
            0x00635A4A,
            0x00BDA57B,
            0x005A4218,
            0x00BD8C31,
            0x00353129,
            0x00948463,
            0x007B6B4A,
            0x00A58C5A,
            0x005A4A29,
            0x009C7B39,
            0x00423110,
            0x00EFAD21,
            0x00181000,
            0x00292100,
            0x009C6B00,
            0x0094845A,
            0x00524218,
            0x006B5A29,
            0x007B6321,
            0x009C7B21,
            0x00DEA500,
            0x005A5239,
            0x00312910,
            0x00CEBD7B,
            0x00635A39,
            0x0094844A,
            0x00C6A529,
            0x00109C18,
            0x00428C4A,
            0x00318C42,
            0x00109429,
            0x00081810,
            0x00081818,
            0x00082910,
            0x00184229,
            0x00A5B5AD,
            0x006B7373,
            0x00182929,
            0x0018424A,
            0x0031424A,
            0x0063C6DE,
            0x0044DDFF,
            0x008CD6EF,
            0x00736B39,
            0x00F7DE39,
            0x00F7EF8C,
            0x00F7E700,
            0x006B6B5A,
            0x005A8CA5,
            0x0039B5EF,
            0x004A9CCE,
            0x003184B5,
            0x0031526B,
            0x00DEDED6,
            0x00BDBDB5,
            0x008C8C84,
            0x00F7F7DE,
            0x00000818,
            0x00081839,
            0x00081029,
            0x00081800,
            0x00082900,
            0x000052A5,
            0x00007BDE,
            0x0010294A,
            0x0010396B,
            0x0010528C,
            0x00215AA5,
            0x0010315A,
            0x00104284,
            0x00315284,
            0x00182131,
            0x004A5A7B,
            0x00526BA5,
            0x00293963,
            0x00104ADE,
            0x00292921,
            0x004A4A39,
            0x00292918,
            0x004A4A29,
            0x007B7B42,
            0x009C9C4A,
            0x005A5A29,
            0x00424214,
            0x00393900,
            0x00595900,
            0x00CA352C,
            0x006B7321,
            0x00293100,
            0x00313910,
            0x00313918,
            0x00424A00,
            0x00526318,
            0x005A7329,
            0x00314A18,
            0x00182100,
            0x00183100,
            0x00183910,
            0x0063844A,
            0x006BBD4A,
            0x0063B54A,
            0x0063BD4A,
            0x005A9C4A,
            0x004A8C39,
            0x0063C64A,
            0x0063D64A,
            0x0052844A,
            0x00317329,
            0x0063C65A,
            0x0052BD4A,
            0x0010FF00,
            0x00182918,
            0x004A884A,
            0x004AE74A,
            0x00005A00,
            0x00008800,
            0x00009400,
            0x0000DE00,
            0x0000EE00,
            0x0000FB00,
            0x004A5A94,
            0x006373B5,
            0x007B8CD6,
            0x006B7BD6,
            0x007788FF,
            0x00C6C6CE,
            0x0094949C,
            0x009C94C6,
            0x00313139,
            0x00291884,
            0x00180084,
            0x004A4252,
            0x0052427B,
            0x00635A73,
            0x00CEB5F7,
            0x008C7B9C,
            0x007722CC,
            0x00DDAAFF,
            0x00F0B42A,
            0x00DF009F,
            0x00E317B3,
            0x00FFFBF0,
            0x00A0A0A4,
            0x00808080,
            0x00FF0000,
            0x0000FF00,
            0x00FFFF00,
            0x000000FF,
            0x00FF00FF,
            0x0000FFFF,
            0x00FFFFFF
        };*/


        private bool NewVersion;

        private int MemCheckTime;
        private DirectXImage[] ImgArr;
        private BmpImage[] BmpArr;

        private PalColor[] Palette;

        private List<int> IndexList;
        private BinaryReader Reader;
        

        private static int WidthBytes(int w)
        {
            return (((w * 8) + 31) / 32) * 4;
        }

        public Texture this[int index] { get { return GetImageSurface(index); } }
        public string FileName { get; set; }
        public int ImageCount { get; private set; }
        public Device GraphicsDevice { get; set; }
        public LibType LibType { get; set; }
        public InternationalVersion InternationalVersion { get; set; }
        public int MaxMemorySize { get; set; }


        public ImageArray()
        {
            FileName = string.Empty;
            LibType = LibType.LoadBmp;
            ImageCount = 0;
            MaxMemorySize = 1024 * 1000; //1M

            GraphicsDevice = null;
            Reader = null;
            ImgArr = null;
            BmpArr = null;
            Palette = new PalColor[256];
            IndexList = new List<int>();

            MemCheckTime = Environment.TickCount;
            NewVersion = false;
            InternationalVersion = InternationalVersion.English;
        }

        /// <summary>
        /// Load all of the surfaces into memory
        /// </summary>
        private void LoadAllData()
        {
            for (int i = 0; i < ImageCount; i++)
            {
                ImageInfoEx imgi = new ImageInfoEx();
                imgi.Width = Reader.ReadUInt16();
                imgi.Height = Reader.ReadUInt16();
                imgi.PosX = Reader.ReadUInt16();
                imgi.PosY = Reader.ReadUInt16();
                if (NewVersion)
                    imgi.ImageVersion = Reader.ReadUInt32();

                byte[] buffer = Reader.ReadBytes(imgi.Width * imgi.Height);

                DirectXImage dximg = new DirectXImage();
                dximg.PosX = imgi.PosX;
                dximg.PosY = imgi.PosY;
                dximg.Surface = CreateTexture(imgi.Width, imgi.Height, buffer);

                ImgArr[i] = dximg;
            }
        }

        /// <summary>
        /// Load the index file which contains the locations of the individual
        /// images in the .WIL file.
        /// </summary>
        /// <param name="idxfile">The filename which shall be loaded.</param>
        private void LoadIndexFile(string idxfile)
        {
            IndexList.Clear();

            if (!File.Exists(idxfile)) return;

            try
            {
                byte[] buffer = File.ReadAllBytes(idxfile);

                using (BinaryReader reader = new BinaryReader(new MemoryStream(buffer), Encoding.ASCII))
                {
                    IndexHeaderEx header = new IndexHeaderEx();
                    header.TitleLength = reader.ReadByte();
                    header.Title = new string(reader.ReadChars(header.TitleLength));
                    reader.BaseStream.Position = 44;
                    header.IndexCount = reader.ReadInt32();

                    Debug.WriteLine("LoadIndexFile header.TitleLength: " + header.TitleLength.ToString());
                    Debug.WriteLine("LoadIndexFile header.Title: " + header.Title);
                    Debug.WriteLine("LoadIndexFile header.IndexCount: " + header.IndexCount.ToString());
    
                    if (NewVersion)
                        header.VersionInfo = reader.ReadUInt32();

                    for (int i = 0; i < header.IndexCount; i++)
                    {
                        int value = reader.ReadInt32();
                        IndexList.Add(value);
                    }
                }
            }
            catch
            {
                // Exception caught...
                return;
            }
        }

        /// <summary>
        /// Load a single image from the file and create a DirectX texture.
        /// </summary>
        /// <param name="position">The position of the image to load.</param>
        /// <param name="dximg">The place to store the texture.</param>
        private void LoadDxImage(int position, ref DirectXImage dximg)
        {
            Reader.BaseStream.Seek(position, SeekOrigin.Begin);

            ImageInfoEx imgi = new ImageInfoEx();
            imgi.Width = Reader.ReadUInt16();
            imgi.Height = Reader.ReadUInt16();
            imgi.PosX = Reader.ReadUInt16();
            imgi.PosY = Reader.ReadUInt16();
            if (NewVersion)
                imgi.ImageVersion = Reader.ReadUInt32();

            byte[] buffer = Reader.ReadBytes(imgi.Width * imgi.Height);

            dximg.PosX = imgi.PosX;
            dximg.PosY = imgi.PosY;
            dximg.Surface = CreateTexture(imgi.Width, imgi.Height, buffer);
        }

        /// <summary>
        /// Load a single image from the file and create a Bitmap image.
        /// </summary>
        /// <param name="position">The position of the image to load.</param>
        /// <param name="pbmpimg">The place to store the bitmap.</param>
        private void LoadBmpImage(int position, ref BmpImage bmpimg)
        {
            Reader.BaseStream.Seek(position, SeekOrigin.Begin);

            ImageInfoEx imgi = new ImageInfoEx();
            imgi.Width = Reader.ReadUInt16();
            imgi.Height = Reader.ReadUInt16();
            imgi.PosX = Reader.ReadUInt16();
            imgi.PosY = Reader.ReadUInt16();
            if (NewVersion)
                imgi.ImageVersion = Reader.ReadUInt32();

            byte[] buffer = Reader.ReadBytes(imgi.Width * imgi.Height);

            bmpimg.Bmp = CreateBitmap(imgi.Width, imgi.Height, buffer);
        }

        /// <summary>
        /// Creatures a simple texture using the currently loaded palette.
        /// </summary>
        /// <param name="nWidth">The width of the texture to create.</param>
        /// <param name="nHeight">The height of the texture to create.</param>
        /// <param name="img">The image in its raw format.</param>
        /// <returns>A texture on success, if a texture cannot be created then null is returned.</returns>
        private Texture CreateTexture(int nWidth, int nHeight, byte[] img)
        {
            if ((nWidth > 0) && (nHeight > 0))
            {
                Texture tex = new Texture(GraphicsDevice, nWidth, nHeight, 1,
                    Usage.None, Format.A8R8G8B8, Pool.Managed);
                GraphicsStream Stream = tex.LockRectangle(0, LockFlags.None);

                int argb;
                byte aa, rr, gg, bb;
                int nByteAlignCX = (((nWidth * 8) + 31) & ~31) >> 3;

                try
                {
                    int S = 0, B = 1, D = 0;
                    int Bytes = nWidth * nHeight;

                    while (S < Bytes)
                    {
                        //argb = (int)Palette[img[(nXIdx) + (nYIdx * nByteAlignCX)]];
                        argb = Palette[img[Bytes + D - (nWidth * B)]].ToArgb();

                        // seperate colors
                        aa = (byte)((argb >> 0x18) & 0xff);
                        rr = (byte)((argb >> 0x10) & 0xff);
                        gg = (byte)((argb >> 8) & 0xff);
                        bb = (byte)(argb & 0xff);

                        aa = (bb == 0 && rr == 0 && gg == 0) ? (byte)0 : (byte)255;
                        // Restore ARGB with new alpha value
                        argb = (aa << 24 | rr << 16 | gg << 8 | bb);
                        Stream.Write(argb);

                        S++;
                        D++;

                        if (S >= (nWidth * B))
                        {
                            B++;
                            D = 0;
                        }
                    }

/*
                    //for (int nYIdx = 0; nYIdx < nHeight; nYIdx++)
                    for (int nYIdx = nHeight - 1; nYIdx >= 0; nYIdx--)    // small change for mdx to draw correct way up..
                    {
                        for (int nXIdx = 0; nXIdx < nWidth; nXIdx++)
                        {
                            // Get index of color from current pixel, then get ARGB from palette with
                            // the index value.
                            //argb = (int)Palette[img[(nXIdx) + (nYIdx * nByteAlignCX)]];
                            argb = Palette[img[(nXIdx) + (nYIdx * nByteAlignCX)]].ToArgb();

                            // seperate colors
                            aa = (byte)((argb >> 0x18) & 0xff);
                            rr = (byte)((argb >> 0x10) & 0xff);
                            gg = (byte)((argb >> 8) & 0xff);
                            bb = (byte)(argb & 0xff);

                            aa = (bb == 0 && rr == 0 && gg == 0) ? (byte)0 : (byte)255;
                            // Restore ARGB with new alpha value
                            argb = (aa << 24 | rr << 16 | gg << 8 | bb);

                            // Write ARGB into the texture
                            //ptDraw.X = nXIdx;
                            //ptDraw.Y = (nHeight - 1) - nYIdx;
                            Stream.Write(argb);
                        }
                    }*/
                }
                catch
                {
                    if (tex != null)
                    {
                        tex.Dispose();
                        tex = null;
                    }
                }
                finally
                {
                    tex.UnlockRectangle(0);
                }

                return tex;
            }

            return null;
        }

        /// <summary>
        /// Creatures a simple bitmap using the currently loaded palette.
        /// </summary>
        /// <param name="nWidth">The width of the bitmap to create.</param>
        /// <param name="nHeight">The height of the bitmap to create.</param>
        /// <param name="img">The image in its raw format.</param>
        /// <returns>A bitmap on success, if a bitmap cannot be created then null is returned.</returns>
        private Bitmap CreateBitmap(int nWidth, int nHeight, byte[] img)
        {
            if ((nWidth > 0) && (nHeight > 0))
            {
                Bitmap bmp = new Bitmap(nWidth, nHeight);

                int argb;
                byte aa, rr, gg, bb;
                int nByteAlignCX = (((nWidth * 8) + 31) & ~31) >> 3;

                try
                {
                    for (int nYIdx = 0; nYIdx < nHeight; nYIdx++)
                    {
                        for (int nXIdx = 0; nXIdx < nWidth; nXIdx++)
                        {
                            // Get index of color from current pixel, then get ARGB from palette with
                            // the index value.
                            //argb = (int)Palette[img[(nXIdx) + (nYIdx * nByteAlignCX)]];
                            argb = Palette[img[(nXIdx) + (nYIdx * nByteAlignCX)]].ToArgb();

                            // seperate colors
                            aa = (byte)((argb >> 0x18) & 0xff);
                            rr = (byte)((argb >> 0x10) & 0xff);
                            gg = (byte)((argb >> 8) & 0xff);
                            bb = (byte)(argb & 0xff);

                            aa = (bb == 0 && rr == 0 && gg == 0) ? (byte)0 : (byte)255;
                            // Restore ARGB with new alpha value
                            argb = (aa << 24 | rr << 16 | gg << 8 | bb);

                            // Write ARGB into the texture
                            bmp.SetPixel(nXIdx, (nHeight - 1) - nYIdx, Color.FromArgb(argb));
                        }
                    }
                }
                catch
                {
                    if (bmp != null)
                    {
                        bmp.Dispose();
                        bmp = null;
                    }
                }
                finally
                {
                }

                return bmp;
            }

            return null;
        }

        private void FreeOldMemorys()
        {
            int n = 0;
            int curtime = Environment.TickCount;
            for (int i = 0; i < ImageCount; i++)
            {
                if (ImgArr[i].Surface != null)
                {
                    if (curtime - ImgArr[i].LatestTime > 5 * 60 * 1000)
                    {
                        ImgArr[i].Surface.Dispose();
                        ImgArr[i].Surface = null;
                        n++;
                    }
                }
            }
        }

        private void FreeOldBmps()
        {
            int n = 0;
            int curtime = Environment.TickCount;
            for (int i = 0; i < ImageCount; i++)
            {
                if (BmpArr[i].Bmp != null)
                {
                    if (curtime - BmpArr[i].LatestTime > 5 * 60 * 1000)
                    {
                        BmpArr[i].Bmp.Dispose();
                        BmpArr[i].Bmp = null;
                        n++;
                    }
                }
            }
        }

        private Texture GetImageSurface(int index)
        {
            switch (LibType)
            {
                case LibType.UseCache:
                    return GetCachedSurface(index);
                case LibType.LoadMemory:
                    if ((index >= 0) && (index < ImageCount))
                        return ImgArr[index].Surface;
                    break;
            }

            return null;
        }

        private Bitmap GetImageBitmap(int index)
        {
            if (LibType == LibType.LoadBmp)
                return GetCachedBitmap(index);
            else
                return null;
        }

        /// <summary>
        /// This will make the class ready for use.
        /// </summary>
        public void Initialize()
        {
            if (FileName == String.Empty)
                throw new Exception("FileName not assigned.");

            //if (LibType != LibType.LoadBmp && Video.IsInitialized == false)
            //    throw new Exception("Video is not initialized.");

            if (File.Exists(FileName) == false)
                throw new FileNotFoundException("FileName not assigned.", FileName);

            if (Reader == null)
                Reader = new BinaryReader(File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.ASCII);
  
            byte[] buffer = Reader.ReadBytes(ImageHeaderEx.MySize);
            if (buffer.Length < ImageHeader.MySize)
                throw new Exception("This is not a valid image library.");

            GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            ImageHeaderEx header = (ImageHeaderEx)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ImageHeaderEx));

            switch (InternationalVersion)
            {
                case InternationalVersion.Taiwan:
                    {
                        WilVersion = WilTaiwan;
                        WixVersion = WixTaiwan;
                        break;
                    }
                case InternationalVersion.Chinese:
                    {
                        WilVersion = WilChinesse;
                        WixVersion = WixChinesse;
                        break;
                    }
                case InternationalVersion.English:
                    {
                        WilVersion = WilEnglish;
                        WixVersion = WixEnglish;
                        break;
                    }
            }

            if (header.VersionInfo != (uint)((WilVersion + header.ImageCount) ^ WilMagicCode)) //ÀÌÀü ¹öÀü ÀÎ °æ¿ì
            {
                NewVersion = false;
                Reader.BaseStream.Seek(-4, SeekOrigin.Current);
            }
            else
                NewVersion = true;

            ImageCount = header.ImageCount;

            if (LibType == LibType.LoadBmp)
            {
                BmpArr = new BmpImage[ImageCount];
                for (int i = 0; i < ImageCount; i++)
                    BmpArr[i] = new BmpImage();
            }
            else
            {
                ImgArr = new DirectXImage[ImageCount];
                for (int i = 0; i < ImageCount; i++)
                    ImgArr[i] = new DirectXImage();
            }

            string idxfile = Path.ChangeExtension(FileName, ".wix");
            LoadPalette();
            if (LibType == LibType.LoadMemory)
                LoadAllData();
            else
                LoadIndexFile(idxfile);
        }

        /// <summary>
        /// We should call this when we are finished with the class.
        /// </summary>
        public void Release()
        {
            if (ImgArr != null)
            {
                for (int i = 0; i < ImageCount; i++)
                {
                    if (ImgArr[i].Surface != null)
                    {
                        ImgArr[i].Surface.Dispose();
                        ImgArr[i].Surface = null;
                    }
                }
            }

            if (BmpArr != null)
            {
                for (int i = 0; i < ImageCount; i++)
                {
                    if (BmpArr[i].Bmp != null)
                    {
                        BmpArr[i].Bmp.Dispose();
                        BmpArr[i].Bmp = null;
                    }
                }
            }

            if (Reader != null)
            {
                Reader.Close();
                Reader = null;
            }

            GC.Collect();
        }

        public void ClearCache()
        {
            if (ImgArr != null)
            {
                for (int i = 0; i < ImageCount; i++)
                {
                    if (ImgArr[i].Surface != null)
                    {
                        ImgArr[i].Surface.Dispose();
                        ImgArr[i].Surface = null;
                    }
                }
            }
            GC.Collect();
        }

        /// <summary>
        /// Load the palette to be used by this class.
        /// </summary>
        public void LoadPalette()
        {
            if (NewVersion)
                Reader.BaseStream.Seek(ImageHeaderEx.MySize, SeekOrigin.Begin);
            else
                Reader.BaseStream.Seek(ImageHeader.MySize, SeekOrigin.Begin);

            //Stream.Seek(PalColor.MySize * 256, SeekOrigin.Current);

            for (int i = 0; i < 256; i++)
            {
                Palette[i] = new PalColor();
                Palette[i].Red = Reader.ReadByte();
                Palette[i].Green = Reader.ReadByte();
                Palette[i].Blue = Reader.ReadByte();
                Palette[i].Alpha = Reader.ReadByte();
            }
        }

        public Texture GetImage(int index, ref int px, ref int py)
        {
            if ((index >= 0) && (index < ImageCount))
            {
                px = ImgArr[index].PosX;
                py = ImgArr[index].PosY;
                return ImgArr[index].Surface;
            }

            return null;
        }

        /// <summary>
        /// function to read the values of the width and height of a image.
        /// </summary>
        /// <param name="index">The index of the image to load.</param>
        /// <param name="width">The width of the image to return.</param>
        /// <param name="height">The height of the image to return.</param>
        /// <returns>Returns true if the sizes could be retrieved for the specified image</returns>
        public bool GetImageSize(int index, ref int width, ref int height)
        {
            if ((index < 0) || (index >= ImageCount) || (index >= IndexList.Count))
                return false;

            // first we will check to see if the image is cached so
            // it will save us some time reading it again!
            if (LibType == LibType.LoadBmp)
            {
                if (BmpArr[index].Bmp != null) // already cached so just check array..
                {
                    width = BmpArr[index].Bmp.Width;
                    height = BmpArr[index].Bmp.Height;
                    return true;
                }
            }
            else
            {
                if (ImgArr[index].Surface != null) // already cached so just check array..
                {
                    SurfaceDescription sd = ImgArr[index].Surface.GetLevelDescription(0);
                    width = sd.Width;
                    height = sd.Height;
                    return true;
                }
            }

            // DOH! okay so it isnt cached we will have to load it from file.
            int position = IndexList[index];
            Reader.BaseStream.Seek(position, SeekOrigin.Begin);

            width = Reader.ReadUInt16();
            height = Reader.ReadUInt16();

            return true;
        }

        /// <summary>
        /// function to read the values of the offsets of a image.
        /// </summary>
        /// <param name="index">The index of the image to load.</param>
        /// <param name="width">The position x of the image to return.</param>
        /// <param name="height">The position y of the image to return.</param>
        /// <returns>Returns true if the offsets could be retrieved for the specified image</returns>
        public bool GetImageOffsets(int index, ref int offsetx, ref int offsety)
        {
            if ((index < 0) || (index >= ImageCount) || (index >= IndexList.Count))
                return false;

            // first we will check to see if the image is cached so
            // it will save us some time reading it again!
            if (LibType == LibType.LoadBmp)
            {
                // can't get offsets from bmp :P
                return false;
            }
            else
            {
                if (ImgArr[index].Surface != null) // already cached so just check array..
                {
                    offsetx = ImgArr[index].PosX;
                    offsety = ImgArr[index].PosY;
                    return true;
                }
            }

            // DOH! okay so it isnt cached we will have to load it from file.
            int position = IndexList[index];
            Reader.BaseStream.Seek(position + 4, SeekOrigin.Begin); // Note: +4 to skip width + height

            offsetx = Reader.ReadUInt16();
            offsety = Reader.ReadUInt16();

            return true;
        }

        public Texture GetCachedImage(int index, ref int px, ref int py)
        {
            if ((index < 0) || (index >= ImageCount))
                return null;

            if (Environment.TickCount - MemCheckTime > 10000)
            {
                MemCheckTime = Environment.TickCount;
                FreeOldMemorys();
            }
            if (ImgArr[index].Surface == null) //cacheµÇ¾î ÀÖÁö ¾ÊÀ½. »õ·Î ÀÐ¾î¾ßÇÔ.
            {
                if (index < IndexList.Count)
                {
                    int position = IndexList[index];
                    LoadDxImage(position, ref ImgArr[index]);
                    ImgArr[index].LatestTime = Environment.TickCount;
                    px = ImgArr[index].PosX;
                    py = ImgArr[index].PosY;
                    return ImgArr[index].Surface;
                }
            }
            else
            {
                ImgArr[index].LatestTime = Environment.TickCount;
                px = ImgArr[index].PosX;
                py = ImgArr[index].PosY;
                return ImgArr[index].Surface;
            }

            return null;
        }

        public Texture GetCachedSurface(int index)
        {
            if ((index < 0) || (index >= ImageCount))
                return null;

            if (Environment.TickCount - MemCheckTime > 10000)
            {
                MemCheckTime = Environment.TickCount;
                FreeOldMemorys();
            }
            if (ImgArr[index].Surface == null) //cacheµÇ¾î ÀÖÁö ¾ÊÀ½. »õ·Î ÀÐ¾î¾ßÇÔ.
            {
                if (index < IndexList.Count)
                {
                    int position = IndexList[index];
                    LoadDxImage(position, ref ImgArr[index]);
                    ImgArr[index].LatestTime = Environment.TickCount;
                    return ImgArr[index].Surface;
                }
            }
            else
            {
                ImgArr[index].LatestTime = Environment.TickCount;
                return ImgArr[index].Surface;
            }

            return null;
        }

        public Bitmap GetCachedBitmap(int index)
        {
            if ((index < 0) || (index >= ImageCount))
                return null;

            if (Environment.TickCount - MemCheckTime > 10000)
            {
                MemCheckTime = Environment.TickCount;
                FreeOldBmps();
            }
            if (BmpArr[index].Bmp == null) //cacheµÇ¾î ÀÖÁö ¾ÊÀ½. »õ·Î ÀÐ¾î¾ßÇÔ.
            {
                if (index < IndexList.Count)
                {
                    int position = IndexList[index];
                    LoadBmpImage(position, ref BmpArr[index]);
                    BmpArr[index].LatestTime = Environment.TickCount;
                    return BmpArr[index].Bmp;
                }
            }
            else
            {
                BmpArr[index].LatestTime = Environment.TickCount;
                return BmpArr[index].Bmp;
            }

            return null;
        }

        /*public void DrawZoom(paper: TCanvas; x, y, index: integer; zoom: real);
        public void DrawZoomEx(paper: TCanvas; x, y, index: integer;
          zoom: real; leftzero: boolean);*/

        #region "IDisposable Support"
        private bool disposedValue;      // To detect redundant calls

        private void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: dispose managed state (managed objects).
                    // Free other state (managed objects).
                    Release();
                }

                // TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                // TODO: set large fields to null.
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}